package model.management;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import model.entity.Product;

import common.Result;

/**
 * Track the instance of the list of product manager
 * 
 * @author Ian Ryu, Paula Chen
 * 
 *         {@invariant products.size() >= 0}
 * 
 */

public enum ProductManager implements Serializable
{
  INSTANCE;

  private List<Product> products = new ArrayList<Product>();

  /**
   * Validates whether the given product can be added to product.
   * 
   * @param product
   *          The product to validate.
   * 
   * @return a result, 
   *        whose status indicates whether the given product can be added to this container.
   * 
   *         {@pre product != null}
   * 
   */
  public static Result validateAddProduct(Product product)
  {
    if (product == null)
    {
      return new Result(false, "Null product.");
    }
    Result validProduct = product.validateProduct();
    if (!validProduct.getStatus())
    {
      return validProduct;
    }
    if (INSTANCE.products.contains(product))
    {
      return new Result(false, "Product already exist.");
    }
    return new Result(true);
  }

  /**
   * Validates whether the given product can be added to this product container. 
   * If so, the product is added to this container.
   * 
   * @param product
   *          The product to be added to this container.
   * 
   * @return a result, indicating whether this operation was successful or not. {@pre product !=
   *         null} {@pre products.size() = (old)products.size() + 1}
   * 
   */
  public static Result addProduct(Product product)
  {
    Result validAdd = validateAddProduct(product);
    if (!validAdd.getStatus())
    {
      return validAdd;
    }
    INSTANCE.products.add(product);
    return new Result(true);
  }

  /**
   * Validates whether the given product can be removed from product.
   * 
   * @param product
   *          The product to validate.
   * 
   * @return a result, whose status indicates 
   *         whether the given product can be removed from this container.
   * 
   *         {@pre product != null}
   * 
   */
  public static Result validateRemoveProduct(Product product)
  {
    if (product == null)
    {
      return new Result(false, "Null product.");
    }
    if (!INSTANCE.products.contains(product))
    {
      return new Result(false, "Product doesn't exist.");
    }
    return new Result(true);
  }

  /**
   * Validates whether the given product can be removed from this product container. 
   * If so, the product is removed from this container.
   * 
   * @param product
   *          The product to be removed from this container.
   * 
   * @return a result, indicating whether this operation was successful or not.
   * 
   *         {@pre product != null} {@pre products.size() = (old)products.size() - 1}
   * 
   */
  public static Result removeProduct(Product product)
  {
    Result validRemove = validateRemoveProduct(product);
    if (!validRemove.getStatus())
    {
      return validRemove;
    }
    INSTANCE.products.remove(product);
    return new Result(true);
  }

  /**
   * Validates whether the given product can be updated.
   * 
   * @param product
   *          The product to validate.
   * 
   * @return a result, whose status indicates whether the given product can be updated.
   * 
   *         {@pre product != null}
   * 
   */
  public static Result validateUpdateProduct(Product oldPorduct, Product newProduct)
  {
    if (oldPorduct == null || newProduct == null)
    {
      return new Result(false, "Null product.");
    }
    Result validProduct = newProduct.validateProduct();
    if (!validProduct.getStatus())
    {
      return validProduct;
    }
    if (!INSTANCE.products.contains(oldPorduct))
    {
      return new Result(false, "Product doesn't exist.");
    }
    return new Result(true);
  }

  /**
   * Validates whether the given product can be updated this product container. 
   * If so, the product is updated.
   * 
   * @param product
   *          The product to be updated this container.
   * 
   * @return a result, indicating whether this operation was successful or not.
   * 
   *         {@pre product != null} {@post products.size == (old)products.size()}
   * 
   */
  public static Result updateProduct(Product oldPorduct, Product newProduct)
  {
    Result validUpdate = validateUpdateProduct(oldPorduct, newProduct);
    if (!validUpdate.getStatus())
    {
      return validUpdate;
    }
    INSTANCE.products.remove(oldPorduct);
    INSTANCE.products.add(newProduct);
    return new Result(true);
  }

  /**
   * @return An iterator of all <code>Product</code>s in the system.
   */
  public static Iterator<Product> getIterator()
  {
    return INSTANCE.products.iterator();
  }

  public static List<Product> getProducts()
  {
    return INSTANCE.products;
  }

  /**
   * Clears all Product data.
   */
  public static void clear()
  {
    INSTANCE.products.clear();
  }
}